from http import server
from http.server import BaseHTTPRequestHandler
from urllib import parse
import base64
import time
import hashlib
import random
import threading
import psutil
import PRF


HTTP_PORT = 8801

VERSION = '1.0.1'

InternalStatus = random.randbytes(64)
InternalStatus_mutex = threading.Lock()
InternalStatus_update_times = 0


def getCurNS():
    try:
        return time.perf_counter_ns()
    except AttributeError:
        return int(time.clock() * 10e9)
    

def int2bytes(value, len):
    max_length = 16
    if len > max_length:
        return None
    return value.to_bytes(byteorder='little', length=max_length)[:len]


def genTimeSpan():
    t = getCurNS()
    bt = int2bytes(t, 8)
    return bt


def update_InternalStatus(salt:bytes):
    global InternalStatus, InternalStatus_update_times
    InternalStatus_mutex.acquire()
    InternalStatus = hashlib.sha512(genTimeSpan() + InternalStatus + salt).digest()
    InternalStatus_update_times += 1
    print('InternalStatus', InternalStatus_update_times)
    InternalStatus_mutex.release()


def gen_rnd(length):
    if length > 255*32:
        return None
    InternalStatus_mutex.acquire()
    prf = PRF.PRF(genTimeSpan(), InternalStatus)
    rnd = prf.get(length)
    InternalStatus_mutex.release()
    return rnd


class myHTTPRequestHandler(BaseHTTPRequestHandler):

    def _response(self, data):
        self.send_response(200)
        self.send_header('Content-type', 'text/plain')
        self.end_headers()
        self.wfile.write(data)

    def do_GET(self):
        url = parse.urlparse(self.path)
        path = url.path
        query_dict = parse.parse_qs(url.query)
        if 'bytes' not in query_dict:
            return
        length = int(query_dict['bytes'][0])
        rnd = gen_rnd(length)
        update_InternalStatus(str(self.client_address).encode() + rnd)
        if path == '/getRandomHex':
            self._response(rnd.hex().encode())
        elif path == '/getRandom':
            self._response(base64.b64encode(rnd))
        else:
            self.send_error(400, 'Unsuported cmd')
        
    def do_POST(self):
        update_InternalStatus(str(self.client_address).encode())
        self.send_error(415, 'Only get is supported')
        return


def start_server():
    print('Listening on port', HTTP_PORT, '......')
    httpd = server.HTTPServer(("0.0.0.0", HTTP_PORT), myHTTPRequestHandler)
    httpd.serve_forever()


class thread_UpdateInternalStatus(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
    
    def genSysInfoMsg(self):
        info = ';'.join([
                str(getCurNS()),
                str(psutil.cpu_percent(percpu=True)),
                str(psutil.cpu_times()),
                str(psutil.virtual_memory()),
                str(psutil.swap_memory()),
                str(psutil.net_io_counters(pernic=True)),
                str(psutil.net_connections()),
                str(psutil.disk_io_counters()),
                str(psutil.pids())
            ])
        return info

    def run(self):
        while True:
            update_InternalStatus(self.genSysInfoMsg().encode())
            time.sleep(8)
            

if __name__ == '__main__':

    print('Random Generator HTTP Service', VERSION)
    
    t = thread_UpdateInternalStatus()
    t.start()

    start_server()